package com.btg.sql.util;

import com.btg.sql.SqlManager;
import com.greenpineyu.fel.FelEngine;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by Administrator on 2016/12/29.
 */
public class SqlHandler {
    protected static final Logger logger = Logger.getLogger(SqlHandler.class);

    private static Map<String, Element> elMap = new ConcurrentHashMap<String, Element>();

    static {
        init();
    }

    /**
     * 获取xml中的sql元素
     */
    private static void init() {
        try {
            SAXReader reader = new SAXReader();
            List<File> list = getFileList();
            Document document;
            Element root;
            if (list != null && list.size() > 0)
                for (File f : list) {
                    document = reader.read(f);
                    root = document.getRootElement();
                    String spaceName = root.attributeValue("name");
                    for (Iterator<?> i = root.elementIterator("sql"); i.hasNext(); ) {
                        Element sql = (Element) i.next();
                        String id = sql.attributeValue("id");
                        elMap.put(spaceName + "." + id, sql);
                    }
                }
        } catch (Exception e) {
            elMap.clear();
            logger.error("加载sql配置失败", e);
        }
    }

    /**
     * 取得sql文件列表
     *
     * @return
     */
    private static List<File> getFileList() {
        List<File> files = new ArrayList<File>();

        String url = SqlManager.instance().getXmlPath();
        try {
            files = getFile(url, ".xml");
        } catch (Exception e) {
            logger.error("读取sql配置文件出错，sql文件配置目录为：" + url);
        }
        return files;
    }

    /**
     * 取得dir下所有以suffix结尾的文件
     *
     * @param dir
     * @return
     */
    private static List<File> getFile(String dir, String suffix) {

        List<File> list = new ArrayList<File>();

        File file = new File(dir);
        if (file.exists()) {
            if (file.isDirectory()) {
                for (File tmp : file.listFiles()) {
                    list.addAll(getFile(tmp.getAbsolutePath(), suffix));
                }
            } else {
                if (file.getName().toLowerCase().endsWith(suffix)) {
                    list.add(file);
                }
            }
        } else {
            logger.error("sql文件目录不存在！", new IOException());
        }
        return list;
    }


    private static String resolveSql(Element el, Map<String, Object> paraMap) {
        String sql = el.asXML();
        Iterator<Element> it = el.elementIterator();
        if (paraMap == null)
            paraMap = new HashMap<String, Object>();
        while (it.hasNext()) {
            Element node = it.next();
            if ("if".equals(node.getName())) {
                String ifStr = node.asXML();
                String value = node.getTextTrim();
                value = "<![CDATA[ " + value + "]]>";
                String k = node.attributeValue(IfType.test.toString());//表达式解析
                if (isNotEmpty(k)) {
                    for (String s : paraMap.keySet()) {
                        k = k.replaceAll(s, String.valueOf(replaceParam(paraMap.get(s))));
                    }
                    if ("true".equals(String.valueOf(FelEngine.instance.eval(k + "")))) {
                        sql = sql.replace(ifStr, value);
                    }
                    break;
                }
                k = node.attributeValue(IfType.isEmp.toString());
                if (isNotEmpty(k)) {
                    Object o = paraMap.get(k);
                    if (o != null) {
                        String v = String.valueOf(paraMap.get(k));
                        if ("".equals(v)) {
                            sql = sql.replace(ifStr, value);
                        }
                    }
                    break;
                }
                k = node.attributeValue(IfType.isNotEmp.toString());
                if (isNotEmpty(k)) {
                    Object o = paraMap.get(k);
                    if (o != null) {
                        String v = String.valueOf(paraMap.get(k));
                        if (v != null && !"".equals(v)) {
                            sql = sql.replace(ifStr, value);
                        }
                    }
                    break;
                }
                k = node.attributeValue(IfType.isNull.toString());
                if (isNotEmpty(k)) {
                    Object o = paraMap.get(k);
                    if (o == null) {
                        sql = sql.replace(ifStr, value);
                    }
                    break;
                }
                k = node.attributeValue(IfType.isNotNull.toString());
                if (isNotEmpty(k)) {
                    Object o = paraMap.get(k);
                    if (o != null) {
                        sql = sql.replace(ifStr, value);
                    }
                    break;
                }

            }
        }
        Document doc;
        try {
            doc = DocumentHelper.parseText(sql);
            sql = doc.getRootElement().getTextTrim();
        } catch (DocumentException e) {
            e.printStackTrace();
        }
        return sql;
    }


    /**
     * 返回处理过的sql
     *
     * @param sql     带占位符sql (select * from t where id = #{id} )
     * @param paraMap sql 参数
     * @return
     */
    public static SqlHelp handlerSqlParam(String sql, Map<String, Object> paraMap) {
        Map<Object, Object> tm = new HashMap<Object, Object>();
        List<Integer> list = new ArrayList<Integer>();
        SqlHelp help = new SqlHelp();
        if (paraMap != null) {
            String str = sql;
            for (String s : paraMap.keySet()) {
                String p = "#{" + s + "}";
                int i = 0;
                while ((i = str.indexOf(p, i)) != -1) {
                    tm.put(i, paraMap.get(s));
                    list.add(i);
                    i = i + p.length();

                }
                sql = sql.replace(p, "?");
            }

            Collections.sort(list);
            Object[] objArr = new Object[tm.size()];
            int i = 0;
            for (int n : list) {
                objArr[i] = tm.get(n);
                i++;
            }
            help.arr = objArr;
        } else {
            help.arr = new Object[0];
        }
        help.sql = sql;
        return help;
    }

    private static String replaceParam(Object obj) {
        if (obj == null)
            return "";
        if (obj instanceof String) {
            return "'" + obj + "'";
        }
        return "";
    }

    private static boolean isNotEmpty(Object obj) {
        if (obj == null) {
            return false;
        }
        if (obj.getClass().isArray()) {
            //判断是否是数组
            return Array.getLength(obj) > 0 ? true : false;
        } else if (obj instanceof Collection) {
            //判断是否是单列集合
            return ((Collection<?>) obj) != null && ((Collection<?>) obj).size() > 0 ? true : false;
        } else if (obj instanceof Map) {
            //判断是否是Map集合
            return ((Map<?, ?>) obj) != null && ((Map<?, ?>) obj).size() > 0 ? true : false;
        } else if (obj instanceof String) {
            //判断是否是字符串
            return ((String) obj) != null && !"".equals(((String) obj)) ? true : false;
        }
        return false;
    }

    static public enum IfType {
        isNull, isNotNull, isEmp, isNotEmp, test
    }

    /**
     * 根据参数取得具体sql及参数结构
     *
     * @param sqlId
     * @param paraMap
     * @return
     */
    public static String getSqlNative(String sqlId, Map<String, Object> paraMap) {
        if (SqlManager.instance().getDevMode())
            init();
        String sql = null;
        try {
            Element el = elMap.get(sqlId);
            sql = resolveSql(el, paraMap);
            //替换@操作符
            for (String s : paraMap.keySet()) {
                if (isNotEmpty(paraMap.get(s)))
                    sql = sql.replaceAll("@" + s, paraMap.get(s) + "");
            }
        } catch (Exception e) {
            logger.error("未找到sqlId 【" + sqlId + "】", e);
        }
        return sql;
    }

    /**
     * 根据参数取得具体sql及参数结构
     *
     * @param sqlId   带占位符sql (select * from t where id = #{id} )
     * @param paraMap
     * @return
     */
    public static SqlHelp getSqlHandler(String sqlId, Map<String, Object> paraMap) {
        String sql = getSqlNative(sqlId, paraMap);
        SqlHelp help = new SqlHelp();
        if (isNotEmpty(sql)) {
            help = handlerSqlParam(sql, paraMap);
        }
        return help;
    }
}
